package fr.ul.ade.secunde.sync;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.util.Log;
import fr.ul.ade.R;
import fr.ul.ade.secunde.util.ResourceManager;
import fr.ul.ade.secunde.util.VersionUtils;

/**
 * Classe responsable de la communication avec l'API d'ADE. Gère la connection
 * (création, maintien, déconnection) ainsi que le décodage des réponses.
 * 
 * @author Michaël Schnell
 * @since Jul 28, 2013
 * 
 * @version 1.3
 */
public class AdeWebInterface {

	/**
	 * Délais de connexion (lecture).
	 */
	private int readTimeout;

	/**
	 * Délais de connexion (établissement).
	 */
	private int connectionTimeout;

	/**
	 * Identifiant de la connexion courante.
	 */
	private String sessionId;

	private Context context;

	/**
	 * Construit un instance en utilisant les valeurs par défaut.
	 * 
	 * @param context
	 */
	public AdeWebInterface(Context context) {
		this(context, context.getResources()
				.getInteger(R.integer.ade_read_timeout), context.getResources()
				.getInteger(R.integer.ade_connection_timeout));
	}

	/**
	 * Construit une instance en utilisant les valeurs fournies pour les délais
	 * de connexion.
	 * 
	 * @param readTimeout
	 *            délai avant l'abandon d'un lecture
	 * @param connectionTimeout
	 *            délai avant l'abandon de l'établissement d'une connexion
	 * @param context
	 *            point d'accès au contexte de l'application
	 */
	public AdeWebInterface(Context context, int readTimeout,
			int connectionTimeout) {
		this.context = context;
		this.readTimeout = readTimeout;
		this.connectionTimeout = connectionTimeout;

		// Alternative pour la résolution d'un bug sur la version FROYO
		// d'Android au niveau des connexion https
		if (!VersionUtils.hasGingerBread()) {
			System.setProperty("http.keepAlive", "false"); //$NON-NLS-1$//$NON-NLS-2$
		}// if
	}

	/**
	 * Ouvre une connection vers ADE.
	 * 
	 * @param login
	 *            login pour la connection
	 * @param password
	 *            mot de passe pour la connection
	 * @throws MalformedURLException
	 *             si {@code url} n'a pas une URL valide
	 * @throws IOException
	 *             si une erreur se produit pendant la connexion
	 * @throws XmlPullParserException
	 *             s'il se produit une erreur lors de la lecture de la réponse
	 *             d'ADE
	 */
	public void connect(String login, String password)
			throws MalformedURLException, IOException, XmlPullParserException {
		String url = this.context.getResources()
				.getString(R.string.ade_url_connect, login, password);
		Log.d("[connect]", "Attempting connect to '" + url + "'");
		this.sessionId = AdeWebXmlParser
				.parseConnectionFeed(buildConnection(url).getInputStream());
		Log.d("[connect]", "Connected with id: " + this.sessionId);
	}

	/**
	 * Ferme la connection courante.
	 * 
	 * @throws MalformedURLException
	 *             si {@code url} n'a pas une URL valide
	 * @throws IOException
	 *             si une erreur se produit pendant la connexion
	 * @throws XmlPullParserException
	 *             s'il se produit une erreur lors de la lecture de la réponse
	 *             d'ADE
	 */
	public void disconnect() throws MalformedURLException,
			XmlPullParserException, IOException {
		String url = this.context.getResources()
				.getString(R.string.ade_url_disconnect, this.sessionId);
		Log.d("[disconnect]", "Attempting disconnect to '" + url + "'");
		String id = AdeWebXmlParser.parseDisconnectionFeed(buildConnection(url)
				.getInputStream());
		if (this.sessionId.equals(id)) {
			Log.d("[disconnect]", "Disconnected with id: " + this.sessionId);
		} else {
			Log.d("[disconnect]",
					String.format("Disconnection error: wrong id. Expected '%s', got '%s'.",
							this.sessionId,
							id));
		}
	}

	/**
	 * Récupère la liste des projets d'ADE. Les valeurs du curseur peuvent être
	 * accédés à l'aide de constantes dans l'interface {@link ProjectQuery}.
	 * 
	 * @return {@link Cursor} contenant les valeurs des projets
	 * 
	 * @throws MalformedURLException
	 *             si {@code url} n'a pas une URL valide
	 * @throws IOException
	 *             si une erreur se produit pendant la connexion
	 * @throws XmlPullParserException
	 *             s'il se produit une erreur lors de la lecture de la réponse
	 *             d'ADE
	 */
	public Cursor getProjects() throws MalformedURLException,
			XmlPullParserException, IOException {
		String url = this.context
				.getResources()
				.getString(R.string.ade_url_get_projects,
						this.sessionId,
						Long.valueOf(this.context.getResources()
								.getInteger(R.integer.ade_get_projects_detail)));
		Log.d("[getProjects]", "Attempting getProjects from '" + url + "'");
		return AdeWebXmlParser.parseGetProjectsFeed(buildConnection(url)
				.getInputStream());
	}

	/**
	 * Fixe le projet sur la connection courante.
	 * 
	 * @param projectId
	 *            identifiant du projet à fixer
	 * 
	 * @throws MalformedURLException
	 *             si {@code url} n'a pas une URL valide
	 * @throws IOException
	 *             si une erreur se produit pendant la connexion
	 * @throws XmlPullParserException
	 *             s'il se produit une erreur lors de la lecture de la réponse
	 *             d'ADE
	 */
	public void setProject(long projectId) throws MalformedURLException,
			XmlPullParserException, IOException {
		String url = this.context.getResources()
				.getString(R.string.ade_url_set_project,
						this.sessionId,
						Long.valueOf(projectId));
		Log.d("[setProject]", "Attempting connect to '" + url + "'");
		AdeWebXmlParser.parseSetProjectFeed(buildConnection(url)
				.getInputStream(), this.sessionId, projectId);
		Log.d("[setProject]", "Project set to: " + projectId + " for session '"
				+ this.sessionId + "'");
	}

	/**
	 * Retourne les ressources associées au numéro étudiant fourni. Les
	 * ressources sont codées sous forme de chaîne (voir {@link ResourceManager}
	 * ).
	 * 
	 * @param studentNumber
	 *            numéro étudiant dont on demande les ressources
	 * @return ressources sous la forme d'une seule {@link String}
	 * 
	 * @throws MalformedURLException
	 *             si {@code url} n'a pas une URL valide
	 * @throws IOException
	 *             si une erreur se produit pendant la connexion
	 * @throws XmlPullParserException
	 *             s'il se produit une erreur lors de la lecture de la réponse
	 *             d'ADE
	 */
	public String getResources(String studentNumber)
			throws MalformedURLException, XmlPullParserException, IOException {
		return null;
	}

	/**
	 * Retourne les cours associées aux ressources fournies entre les dates
	 * fournies. Les cours peuvent êtres accédés grâce aux constances dans
	 * l'interface {@link LessonQuery}.
	 * 
	 * @param from
	 *            date de début (incluse)
	 * @param to
	 *            date de fin (incluse)
	 * @param resources
	 *            ressources des emploi du temps ADE à récupérer
	 * @return {@link Cursor} contenant les cours demandés
	 * 
	 * @throws MalformedURLException
	 *             si {@code url} n'a pas une URL valide
	 * @throws IOException
	 *             si une erreur se produit pendant la connexion
	 * @throws XmlPullParserException
	 *             s'il se produit une erreur lors de la lecture de la réponse
	 *             d'ADE
	 */
	public Cursor getEvents(Date from, Date to, String resources)
			throws MalformedURLException, XmlPullParserException, IOException {
		final Resources appResources = this.context.getResources();
		final SimpleDateFormat format = new SimpleDateFormat(
				appResources.getString(R.string.ade_date_format));
		final String fForm = format.format(from);
		final String fTo = format.format(to);

		String url = appResources.getString(R.string.ade_url_get_events,
				this.sessionId,
				Long.valueOf(appResources
						.getInteger(R.integer.ade_get_events_detail)),
				resources,
				fForm,
				fTo);

		Log.d("[getEvents]", "Attempting getEvents from '" + url + "'");
		return AdeWebXmlParser.parseGetEventsFeed(buildConnection(url)
				.getInputStream());
	}

	/**
	 * Initialise une {@link URLConnection} vers l'{@link URL} fournie.
	 * 
	 * @param url
	 *            destionation de la connection
	 * @throws MalformedURLException
	 *             si {@code url} n'a pas une URL valide
	 * @throws IOException
	 *             si une erreur se produit pendant la connexion
	 */
	private URLConnection buildConnection(String url)
			throws MalformedURLException, IOException {
		URL u = new URL(url);
		URLConnection conn = u.openConnection();
		conn.setReadTimeout(this.readTimeout);
		conn.setConnectTimeout(this.connectionTimeout);
		conn.connect();
		return conn;
	}

}// class AdeWebInterface